package com.atguigu.srb.core.service.impl;

import com.atguigu.srb.common.exception.Assert;
import com.atguigu.srb.common.result.ResponseEnum;
import com.atguigu.srb.core.enums.LendStatusEnum;
import com.atguigu.srb.core.enums.TransTypeEnum;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.mapper.LendItemMapper;
import com.atguigu.srb.core.mapper.LendMapper;
import com.atguigu.srb.core.mapper.UserAccountMapper;
import com.atguigu.srb.core.pojo.bo.TransFlowBO;
import com.atguigu.srb.core.pojo.entity.Lend;
import com.atguigu.srb.core.pojo.entity.LendItem;
import com.atguigu.srb.core.pojo.vo.InvestVO;
import com.atguigu.srb.core.service.*;
import com.atguigu.srb.core.util.LendNoUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 标的出借记录表 服务实现类
 * </p>
 *
 * @author lwm
 * @since 2022-02-10
 */
@Service
@Slf4j
public class LendItemServiceImpl extends ServiceImpl<LendItemMapper, LendItem> implements LendItemService {

    @Resource
    private LendMapper lendMapper;
    @Resource
    private UserAccountService userAccountService;
    @Resource
    private LendService lendService;
    @Resource
    private UserBindService userBindService;
    @Resource
    private TransFlowService transFlowService;
    @Resource
    private UserAccountMapper userAccountMapper;

    //会员投资的表单生成
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String commitInvest(InvestVO investVO) {
        Long lendId = investVO.getLendId();
        Lend lend = lendMapper.selectById(lendId);

        //校验标的状态是否合法---是否为募集中
        Assert.equals(LendStatusEnum.INVEST_RUN.getStatus(), lend.getStatus(),
                ResponseEnum.LEND_INVEST_ERROR);

        //标的不能超卖：(已投金额 + 本次投资金额 )>=标的金额（超卖）
        BigDecimal investAmount = new BigDecimal(investVO.getInvestAmount());
        BigDecimal investSum = investAmount.add(lend.getInvestAmount());
        Assert.isTrue(lend.getAmount().compareTo(investSum) >= 0, ResponseEnum.LEND_FULL_SCALE_ERROR);

        //账户可用余额充足：当前用户的余额 >= 当前用户的投资金额（可以投资）
        Long investUserId = investVO.getInvestUserId();
        BigDecimal balance = userAccountService.getAccount(investUserId);
        Assert.isTrue(balance.compareTo(investAmount) >= 0,
                ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);

        //新增投资项记录 LendItem
        LendItem lendItem = new LendItem();
        //生成投资订单唯一流水号
        String lendItemNo = LendNoUtils.getLendItemNo();
        lendItem.setLendItemNo(lendItemNo);
        lendItem.setLendId(lendId);
        lendItem.setInvestUserId(investUserId);
        lendItem.setInvestName(investVO.getInvestName());
        lendItem.setInvestAmount(investAmount);
        BigDecimal lendYearRate = lend.getLendYearRate();
        lendItem.setLendYearRate(lendYearRate);
        lendItem.setInvestTime(LocalDateTime.now());
        lendItem.setLendStartDate(lend.getLendStartDate());
        lendItem.setLendEndDate(lend.getLendEndDate());

        //预期收益
        BigDecimal expectAmount = lendService.getInterestCount(investAmount,
                lendYearRate, lend.getPeriod(), lend.getReturnMethod());
        lendItem.setExpectAmount(expectAmount);
        lendItem.setRealAmount(new BigDecimal("0"));//实际收益,这个功能不做，提前放款这里就会不一样
        lendItem.setStatus(0);//默认状态：刚刚创建
        baseMapper.insert(lendItem);

        //组装用户投资相关的参数
        //获取投资人的绑定协议号
        String investUserBindCode = userBindService.getBindCodeByUserId(investUserId);
        //获取借款人的绑定协议号
        String borrowerBindCode = userBindService.getBindCodeByUserId(lend.getUserId());
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("agentId", HfbConst.AGENT_ID);
        paramMap.put("voteBindCode", investUserBindCode);
        paramMap.put("benefitBindCode", borrowerBindCode);
        paramMap.put("agentProjectCode", lend.getLendNo());//项目标号
        paramMap.put("agentProjectName", lend.getTitle());
        //在资金托管平台上的投资订单的唯一编号，要和lendItemNo保持一致。
        paramMap.put("agentBillNo", lendItemNo);//订单编号
        paramMap.put("voteAmt", investAmount);
        paramMap.put("votePrizeAmt", "0");
        paramMap.put("voteFeeAmt", "0");
        paramMap.put("projectAmt", lend.getAmount()); //标的总金额
        paramMap.put("note", "");
        paramMap.put("notifyUrl", HfbConst.INVEST_NOTIFY_URL); //检查常量是否正确
        paramMap.put("returnUrl", HfbConst.INVEST_RETURN_URL);
        paramMap.put("timestamp", RequestHelper.getTimestamp());
        String sign = RequestHelper.getSign(paramMap);
        paramMap.put("sign", sign);
        //生产并返回用户投资表单
        return FormHelper.buildForm(HfbConst.INVEST_URL, paramMap);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void notify(Map<String, Object> paramMap) {
        //先拿到交易流水号，判断幂等性--避免多次接受回调请求而产生不同结果
        String agentBillNo = (String) paramMap.get("agentBillNo");
        boolean exist = transFlowService.exist(agentBillNo);
        if (exist) {
            log.warn("幂等性返回");
            return;
        }
        //拿到投资人的bindCode，投资金额
        String voteBindCode = (String) paramMap.get("voteBindCode");
        String voteAmt = (String) paramMap.get("voteAmt");
        //投资成功的情况
        boolean success = "0001".equals(paramMap.get("resultCode"));
        if (success) {
            //修改账户金额，从可用余额中减去投资金额，在冻结金额中增加投资进入
            userAccountMapper.updateAccount(
                    voteBindCode,
                    new BigDecimal("-" + voteAmt),
                    new BigDecimal(voteAmt));

            //修改投资记录的状态
            LambdaQueryWrapper<LendItem> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(LendItem::getLendItemNo, agentBillNo);
            LendItem lendItem = baseMapper.selectOne(wrapper);
            lendItem.setStatus(1);//1表示投资人已支付投资金额
            baseMapper.updateById(lendItem);

            //修改标的记录，投资人数，已投金额
            Long lendId = lendItem.getLendId();
            Lend lend = lendMapper.selectById(lendId);
            lend.setInvestNum(lend.getInvestNum() + 1);
            lend.setInvestAmount(lend.getInvestAmount().add(new BigDecimal(voteAmt)));
            lendMapper.updateById(lend);
            //新增交易流水
            TransFlowBO transFlowBO = new TransFlowBO();
            transFlowBO.setAgentBillNo(agentBillNo);
            transFlowBO.setBindCode(voteBindCode);
            transFlowBO.setAmount(new BigDecimal(voteAmt));
            transFlowBO.setTransTypeEnum(TransTypeEnum.INVEST_LOCK);
            transFlowBO.setMemo("用户投资，项目编号：" + lend.getLendNo() + "，项目名称：" + lend.getTitle());
            transFlowService.saveTransFlow(transFlowBO);
        }

    }

    //根据lendId和支付状态获取LendItem列表
    @Override
    public List<LendItem> selectByLendId(Long lendId, Integer status) {
        LambdaQueryWrapper<LendItem> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LendItem::getLendId, lendId)
                .eq(status != null, LendItem::getStatus, status);
        return baseMapper.selectList(wrapper);
    }
}
